package com.gombe.credit.app.sdk.dec.util

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.usage.StorageStatsManager
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.ImageFormat
import android.graphics.Point
import android.hardware.Sensor
import android.hardware.SensorManager
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.os.Build
import android.os.Environment
import android.os.StatFs
import android.os.SystemClock
import android.os.storage.StorageManager
import android.provider.Settings
import android.text.TextUtils
import android.util.Base64
import android.view.WindowManager
import org.json.JSONArray
import org.json.JSONObject
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.InputStreamReader
import java.math.BigDecimal
import java.math.RoundingMode
import java.net.Inet4Address
import java.net.NetworkInterface
import java.net.SocketException
import java.nio.charset.StandardCharsets
import java.util.Locale
import java.util.TimeZone
import java.util.UUID
import java.util.zip.GZIPOutputStream
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.sqrt

/**
 * 设备数据所有方法
 */
object GombeCreditUserDecUtils {

    @SuppressLint("HardwareIds")
    fun takeDec3Params1(context: Context): String {
        return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID) ?: ""
    }

    fun takeDec3Params2(context: Context): String {
        return ""
    }

    fun takeDec1AndDec3Params1(): String {
        return try {
            Locale.getDefault().language
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params3(): String {
        return try {
            Locale.getDefault().displayLanguage
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params4(): String {
        return try {
            Locale.getDefault().isO3Language
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params5(): String {
        return try {
            Locale.getDefault().isO3Country
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params6(): String {
        return try {
            TimeZone.getDefault().id
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    @SuppressLint("ServiceCast")
    fun takeDec1AndDec3Params2(context: Context): String {
        return try {
            val connectivityManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val network = connectivityManager.activeNetworkInfo
            if (network != null && network.isConnected) {
                when (network.type) {
                    ConnectivityManager.TYPE_WIFI -> "WIFI"
                    ConnectivityManager.TYPE_MOBILE -> "MOBILE"
                    else -> ""
                }
            } else {
                ""
            }
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec1AndDec3Params3(context: Context): String {
        return ""
    }

    fun takeDec3Params7(context: Context): String {
        return try {
            val applicationInfo = context.applicationInfo
            if (applicationInfo != null && applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
                "YES"
            } else {
                "NO"
            }
        } catch (e: Exception) {
            e.printStackTrace()
            "NO"
        }
    }

    fun takeDec3Params8(): String {
        return try {
            SystemClock.elapsedRealtime().toString()
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params9(): String {
        return try {
            (System.currentTimeMillis() - SystemClock.elapsedRealtime()).toString()
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params10(context: Context): String {
        return try {
            val packageInfo = context.packageManager.getPackageInfo(
                context.packageName,
                PackageManager.GET_ACTIVITIES
            )
            packageInfo.versionName ?: "1.0"
        } catch (e: Exception) {
            e.printStackTrace()
            "1.0"
        }
    }

    fun takeDec3Params11(context: Context): String {
        return try {
            val packageInfo = context.packageManager.getPackageInfo(
                context.packageName,
                PackageManager.GET_ACTIVITIES
            )
            packageInfo.versionCode.toString()
        } catch (e: Exception) {
            e.printStackTrace()
            "1"
        }
    }

    fun takeDec3Params12(context: Context): String {
        return ""
    }

    fun takeDec1Params1(): String {
        return try {
            Build.SUPPORTED_ABIS[0]
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec1Params2(context: Context): String {
        return try {
            context.resources.displayMetrics.heightPixels.toString()
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec1Params3(context: Context): String {
        return try {
            context.resources.displayMetrics.widthPixels.toString()
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec1Params4(): String {
        return Locale.getDefault().getISO3Country()
    }

    fun takeDec3Params13(context: Context): String {
        val jsonArray = JSONArray()
        try {
            val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
            val sensors = sensorManager.getSensorList(Sensor.TYPE_ALL)
            for (sensor in sensors) {
                val jsonObject = JSONObject().apply {
                    put("type", sensor.type.toString())
                    put("name", sensor.name.checkString())
                    put("version", sensor.version.toString())
                    put("maxRange", sensor.maximumRange.toString().checkString())
                    put("vendor", sensor.vendor.checkString())
                    put("minDelay", sensor.minDelay.toString().checkString())
                    put("power", sensor.power.toString().checkString())
                    put("resolution", sensor.resolution.toString().checkString())
                }
                jsonArray.put(jsonObject)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return jsonArray.toString()
    }

    private fun chrotmo(): Boolean {
        val buildTags = Build.TAGS
        return buildTags != null && buildTags.contains("test-keys")
    }

    private fun chrotmonext(): Boolean {
        val paths = arrayOf(
            "/system/app/Superuser.apk",
            "/sbin/su",
            "/system/bin/su",
            "/system/xbin/su",
            "/data/local/xbin/su",
            "/data/local/bin/su",
            "/system/sd/xbin/su",
            "/system/bin/failsafe/su",
            "/data/local/su"
        )
        return paths.any { File(it).exists() }
    }

    private fun chrotmolast(): Boolean {
        var process: Process? = null
        return try {
            process = Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su"))
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            reader.readLine() != null
        } catch (t: Throwable) {
            false
        } finally {
            process?.destroy()
        }
    }

    private fun isDrot(): Boolean {
        return chrotmo() || chrotmonext() || chrotmolast()
    }

    fun takeDec3Params14(): String {
        return if (isDrot()) "YES" else "NO"
    }


    fun takeDec3Params15(context: Context): String {
        return try {
            val config = context.resources.configuration
            config.keyboard.toString().checkString()
        } catch (e: Exception) {
            e.printStackTrace()
            ""
        }
    }

    fun takeDec3Params16(): String {
        val isMatch = (
                Build.BRAND.startsWith("generic") ||
                        Build.FINGERPRINT.startsWith("generic") ||
                        Build.FINGERPRINT.startsWith("unknown") ||
                        Build.HARDWARE.contains("goldfish") ||
                        Build.HARDWARE.contains("ranchu") ||
                        Build.MODEL.contains("google_sdk") ||
                        Build.MODEL.contains("Emulator") ||
                        Build.MODEL.contains("Android SDK built for x86") ||
                        Build.MANUFACTURER.contains("Genymotion") ||
                        (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
                        "google_sdk" == Build.PRODUCT)

        return if (isMatch) "YES" else "NO"
    }


    @SuppressLint("MissingPermission")
    fun takeDec3Params17(context: Context): String {
        return ""
    }

    fun takeDec1Params5(context: Context): String {
        val cameraId = hfcm(context)
        return cmpix(context, cameraId)
    }

    fun takeDec1Params6(context: Context): String {
        val cameraId = hbcm(context)
        return cmpix(context, cameraId)
    }

    private fun hfcm(context: Context): Int {
        var cid = -1
        val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
        try {
            val cameraIds = cameraManager.cameraIdList
            for (cID in cameraIds) {
                if (cID == CameraCharacteristics.LENS_FACING_BACK.toString()) {
                    cid = cID.toInt()
                    break
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return cid
    }

    private fun hbcm(context: Context): Int {
        var cid = -1
        try {
            val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
            val cameraIds = cameraManager.cameraIdList
            for (cID in cameraIds) {
                if (cID == CameraCharacteristics.LENS_FACING_FRONT.toString()) {
                    cid = cID.toInt()
                    break
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return cid
    }

    private fun cmpix(context: Context, paramInt: Int): String {
        var pixelValue = "0"
        if (paramInt == -1)
            return pixelValue

        try {
            val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
            val cameraCharacteristics = cameraManager.getCameraCharacteristics(paramInt.toString())
            val streamConfigurationMap =
                cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
            val sizes = streamConfigurationMap?.getOutputSizes(ImageFormat.JPEG)
            if (!sizes.isNullOrEmpty()) {
                val fistSize = sizes[0]
                val gWidth = fistSize.width
                val gHeight = fistSize.height
                val pixels = (gWidth * gHeight / 10000)
                pixelValue = pixels.toString()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return pixelValue
    }

    fun takeDec3Params18(context: Context): String {
        var sizeStr = ""
        try {
            val point = Point()
            val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
            wm.defaultDisplay.getRealSize(point)
            val dm = context.resources.displayMetrics
            val x = (point.x / dm.xdpi.toDouble()).pow(2.0)
            val y = (point.y / dm.ydpi.toDouble()).pow(2.0)
            val screenInches = sqrt(x + y)
            sizeStr = screenInches.toString()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return sizeStr
    }

    fun takeDec1Params7(): String {
        var macAddress = ""
        try {
            val interfaces = NetworkInterface.getNetworkInterfaces()
            while (interfaces.hasMoreElements()) {
                val networkInterface = interfaces.nextElement()
                val hardwareAddress = networkInterface.hardwareAddress
                if (hardwareAddress != null && hardwareAddress.isNotEmpty()) {
                    val stringBuilder = StringBuilder()
                    for (b in hardwareAddress) {
                        stringBuilder.append(String.format("%02X:", b))
                    }
                    if (stringBuilder.isNotEmpty()) {
                        stringBuilder.deleteCharAt(stringBuilder.length - 1)
                    }
                    var currentMac = stringBuilder.toString()
                    if (!networkInterface.name.equals("wlan0", ignoreCase = true)) {
                        currentMac = macAddress
                    }
                    macAddress = currentMac
                }
            }
        } catch (e: SocketException) {
            e.printStackTrace()
        }
        return macAddress
    }

    fun takeDec1Params8(context: Context): String {
        try {
            val connectivityManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val info: NetworkInfo? = connectivityManager.activeNetworkInfo
            if (info != null && info.isConnected) {
                if (info.type == ConnectivityManager.TYPE_MOBILE) {
                    val interfaces = NetworkInterface.getNetworkInterfaces()
                    while (interfaces.hasMoreElements()) {
                        val networkInterface = interfaces.nextElement()
                        val enumIpAddr = networkInterface.inetAddresses
                        while (enumIpAddr.hasMoreElements()) {
                            val inetAddress = enumIpAddr.nextElement()
                            if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
                                return inetAddress.hostAddress ?: ""
                            }
                        }
                    }
                } else if (info.type == ConnectivityManager.TYPE_WIFI) {
                    val wifiManager =
                        context.getSystemService(Context.WIFI_SERVICE) as WifiManager
                    val wifiInfo: WifiInfo? = wifiManager.connectionInfo
                    wifiInfo?.let {
                        return toids(wifiInfo.ipAddress)
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return ""
    }

    private fun toids(ip: Int): String {
        return "${ip and 0xFF}.${ip shr 8 and 0xFF}.${ip shr 16 and 0xFF}.${ip shr 24 and 0xFF}"
    }

    fun takeDec5Params1(context: Context): String {
        return try {
            val jsonObject = JSONObject()
            val wifiManager =
                context.getSystemService(Context.WIFI_SERVICE) as WifiManager
            val wifiInfo: WifiInfo? = wifiManager.connectionInfo
            wifiInfo?.let {
                val bssid = wifiInfo.bssid.checkString()
                val ssid = (wifiInfo.ssid.replace("\"", "")).checkString()
                val mac = takeDec1Params7().checkString()
                val safeBssid = if (TextUtils.isEmpty(bssid)) "" else bssid
                jsonObject.put("bssid", safeBssid)
                jsonObject.put("ssid", ssid)
                jsonObject.put("mac", mac)
            }
            jsonObject.toString()
        } catch (e: Exception) {
            JSONObject().toString()
        }
    }

    fun takeDec3Params19(context: Context): String {
        return try {
            val connectivityManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
            if (activeNetwork != null && activeNetwork.type == ConnectivityManager.TYPE_VPN) {
                "YES"
            } else {
                "NO"
            }
        } catch (e: Exception) {
            e.printStackTrace()
            "NO"
        }
    }

    fun takeDec3Params20(): String {
        return try {
            val proxyHost = System.getProperty("http.proxyHost")
            val proxyPort = System.getProperty("http.proxyPort")
            if (proxyHost != null && proxyPort != null) {
                "YES"
            } else {
                "NO"
            }
        } catch (e: Exception) {
            e.printStackTrace()
            "NO"
        }
    }

    //-------------------------------------------------------------

    fun takeDec4Params1(context: Context): String {
        return try {
            val jsonObject = caInerSiz(context)
            jsonObject.getString("total")
        } catch (e: Exception) {
            //
            "0"
        }
    }

    fun takeDec4Params2(context: Context): String {
        return try {
            val jsonObject = caInerSiz(context)
            jsonObject.getString("available")
        } catch (e: Exception) {
            //
            "0"
        }
    }

    @SuppressLint("DiscouragedPrivateApi", "UsableSpace")
    private fun caInerSiz(context: Context): JSONObject {
        val unit = 1024f
        val version = Build.VERSION.SDK_INT
        val storeObj = JSONObject()
        try {
            if (version < Build.VERSION_CODES.M) {
                val storageManager =
                    context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
                val getVolumeList = StorageManager::class.java.getDeclaredMethod("getVolumeList")
                val volumeList = getVolumeList.invoke(storageManager) as Array<*>
                var totalSize = 0L
                var availableSize = 0L
                volumeList.first()?.javaClass?.getDeclaredMethod("getPathFile")?.apply {
                    for (volume in volumeList) {
                        val file = invoke(volume) as File?
                        file?.let {
                            totalSize += file.totalSpace
                            availableSize += file.usableSpace
                        }
                    }
                }
                val totalStore = funit(totalSize.toFloat(), unit)
                val availableStore = funit(availableSize.toFloat(), unit)
                storeObj.put("total", totalStore)
                storeObj.put("available", availableStore)
                storeObj.put("used", "0")
            } else {
                val storageManager =
                    context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
                var total = 0L
                var used = 0L
                val getVolumes = StorageManager::class.java.getDeclaredMethod("getVolumes")
                val getVolumeInfo = getVolumes.invoke(storageManager) as List<*>
                for (objInfo in getVolumeInfo) {
                    objInfo?.let { obj ->
                        val getType = obj.javaClass.getField("type")
                        val type = getType.getInt(obj)
                        if (type == 1) {
                            var totalSize = 0L
                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                val getFsUuid =
                                    obj.javaClass.getDeclaredMethod("getFsUuid")
                                val fsUuid = getFsUuid.invoke(obj) as String?
                                totalSize =
                                    toaSiz(
                                        context,
                                        fsUuid ?: StorageManager.UUID_DEFAULT.toString()
                                    )
                            }
                            val isMountedReadable =
                                obj.javaClass.getDeclaredMethod("isMountedReadable")
                            val readable = isMountedReadable.invoke(obj) as Boolean
                            if (readable) {
                                val file = obj.javaClass.getDeclaredMethod("getPath")
                                val f = file.invoke(obj) as File?
                                f?.let {
                                    if (totalSize == 0L) {
                                        totalSize = f.totalSpace
                                    }
                                    used += totalSize - f.freeSpace
                                    total += totalSize
                                }
                            }
                        } else if (type == 0) {
                            val isMountedReadable =
                                obj.javaClass.getDeclaredMethod("isMountedReadable")
                            val readable = isMountedReadable.invoke(obj) as Boolean
                            if (readable) {
                                val file = obj.javaClass.getDeclaredMethod("getPath")
                                val f = file.invoke(obj) as File?
                                f?.let {
                                    used += f.totalSpace - f.freeSpace
                                    total += f.totalSpace
                                }
                            }
                        }
                    }
                }
                val totalStore = funit(total.toFloat(), unit)
                val usedStore = funit(used.toFloat(), unit)
                val availableStore = funit((total - used).toFloat(), unit)
                storeObj.put("total", totalStore)
                storeObj.put("available", availableStore)
                storeObj.put("used", usedStore)
            }
        } catch (e: Exception) {
            //
        }
        return storeObj
    }

    private fun toaSiz(context: Context, fsUuid: String): Long {
        return try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                var id = StorageManager.UUID_DEFAULT
                if (fsUuid.isNotBlank()) {
                    id = UUID.fromString(fsUuid)
                }
                val stats =
                    context.getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager?
                stats?.getTotalBytes(id) ?: -1
            } else {
                -1
            }
        } catch (e: Exception) {
            //
            -1
        }
    }

    private fun funit(size: Float, unit: Float): String {
        val units = arrayOf("B", "KB", "MB", "GB", "TB")
        var index = 0
        var newSize = size
        while (newSize > unit && index < 4) {
            newSize /= unit
            index++
        }
        val resultDec = BigDecimal(newSize.toDouble())
            .setScale(2, RoundingMode.HALF_UP)
        return "${resultDec.toPlainString()}${units[index]}"
    }

    fun takeDec4Params3(paramContext: Context): String {
        return try {
            val memoryInfo = ActivityManager.MemoryInfo()
            (paramContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(
                memoryInfo
            )
            funit(memoryInfo.availMem.toFloat(), 1024f)
        } catch (e: java.lang.Exception) {
            //
            "0"
        }
    }

    fun takeDec4Params4(paramContext: Context): String {
        return try {
            val memoryInfo = ActivityManager.MemoryInfo()
            (paramContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(
                memoryInfo
            )
            val didUsed = memoryInfo.totalMem - memoryInfo.availMem
            funit(didUsed.toFloat(), 1024f)
        } catch (e: java.lang.Exception) {
            //
            "0"
        }
    }

    fun takeDec1AndDec4Params1(paramContext: Context): String {
        return try {
            val memoryInfo = ActivityManager.MemoryInfo()
            (paramContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(
                memoryInfo
            )
            funit(memoryInfo.totalMem.toFloat(), 1024f)
        } catch (e: java.lang.Exception) {
            //
            "0"
        }
    }

    fun takeDec4Params5(): String {
        return try {
            val file = Environment.getDataDirectory()
            val statFs = StatFs(file.path)
            funit(
                (statFs.availableBlocksLong * statFs.blockSizeLong).toFloat(),
                1024f
            )
        } catch (e: java.lang.Exception) {
            //
            "0"
        }
    }

    fun takeDec4Params6(): String {
        return try {
            val file = Environment.getDataDirectory()
            val statFs = StatFs(file.path)
            val total = statFs.blockCountLong * statFs.blockSizeLong
            val avail = statFs.availableBlocksLong * statFs.blockSizeLong
            val didUsed = total - avail
            funit(didUsed.toFloat(), 1024f)
        } catch (e: java.lang.Exception) {
            //
            "0"
        }
    }

    fun takeDec1AndDec4Params2(): String {
        return try {
            val file = Environment.getDataDirectory()
            val statFs = StatFs(file.path)
            funit(
                (statFs.blockCountLong * statFs.blockSizeLong).toFloat(),
                1024f
            )
        } catch (e: java.lang.Exception) {
            //
            "0"
        }
    }

    fun takeDec4Params7(): String {
        return if (Environment.getExternalStorageState() == "mounted") {
            "YES"
        } else "NO"
    }

    fun takeDec4Params8(): String {
        return "NO"
    }

    fun takeDec4Params9(): String {
        return try {
            if (Environment.getExternalStorageState().equals("mounted", ignoreCase = true)) {
                val file = Environment.getExternalStorageDirectory()
                val statFs = StatFs(file.path)
                val l = statFs.blockSizeLong
                return funit((statFs.blockCountLong * l).toFloat(), 1024f)
            }
            "0GB"
        } catch (e: java.lang.Exception) {
            //
            "0GB"
        }
    }

    fun String?.checkString(): String {
        return this ?: ""
    }

    fun Int.checkInt(): Int {
        return max(this, 0)
    }

    fun String?.string2Long(): Long {
        if (this.isNullOrBlank()) return 0L
        return try {
            this.toLong()
        } catch (e: Exception) {
            0L
        }
    }

    fun String.takeZipString(): String {
        if (isBlank()) return ""

        return try {
            val utf8Bytes = this.toByteArray(StandardCharsets.UTF_8)
            ByteArrayOutputStream().use { outputStream ->
                GZIPOutputStream(outputStream).apply {
                    write(utf8Bytes)
                    close()
                }
                Base64.encodeToString(
                    outputStream.toByteArray(),
                    Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING
                )
            }
        } catch (e: Exception) {
            ""
        }
    }

    fun takeDec7MobileKeyList(): List<String> {
        return listOf("orange", "free", "money", "wave", "pret", "loan", "info", "topemprun")
    }

    fun takeDec7ContentKeyList(): List<String> {
        return listOf("money", "loan", "pret", "verification", "de passe", "credit")
    }
}